#include "csapp.h"
#pragma GCC diagnostic ignored "-Wformat-overflow="
void clienterror(int connfd,char *filename,char *errorcode,char *errormsg,char *error){	
	char buf[MAXLINE],body[MAXLINE];
	sprintf(body,"<html><title>Tiny Error</title>");
	sprintf(body,"%s<body bgcolor=""ffffff"">\r\n",body);
	sprintf(body,"%s%s:%s\r\n",body,errorcode,errormsg);
	sprintf(body,"%s<p>%s: %s\r\n",body,error,filename);
	sprintf(body,"%s<hr><em>The Tiny Web server</em>\r\n",body);

	sprintf(buf,"HTTP/1.0 %s %s\r\n",errorcode,errormsg);
	Rio_writen(connfd,buf,sizeof buf);
	sprintf(buf,"Content-type: text/html\r\n");
	Rio_writen(connfd,buf,sizeof buf);
	sprintf(buf,"Content-length: %d\r\n\r\n",(int)strlen(body));
	Rio_writen(connfd,buf,strlen(buf));
	Rio_writen(connfd,body,strlen(body));

	return;
}
void read_requesthdrs(rio_t *rp){
	char buf[MAXLINE];
	Rio_readlineb(rp,buf,MAXLINE);
	while(strcmp(buf,"\r\n")){
		Rio_readlineb(rp,buf,MAXLINE);
		printf("%s",buf);
	}
	return;
}
int parse_uri(char *uri, char *filename, char *cgiargs) 
{
    char *ptr;

    if (!strstr(uri, "cgi-bin")) {  /* Static content */ //line:netp:parseuri:isstatic
	strcpy(cgiargs, "");                             //line:netp:parseuri:clearcgi
	strcpy(filename, ".");                           //line:netp:parseuri:beginconvert1
	strcat(filename, uri);                           //line:netp:parseuri:endconvert1
	if (uri[strlen(uri)-1] == '/')                   //line:netp:parseuri:slashcheck
	    strcat(filename, "home.html");               //line:netp:parseuri:appenddefault
	return 1;
    }
    else {  /* Dynamic content */                        //line:netp:parseuri:isdynamic
	ptr = index(uri, '?');                           //line:netp:parseuri:beginextract
	if (ptr) {
	    strcpy(cgiargs, ptr+1);
	    *ptr = '\0';
	}
	else 
	    strcpy(cgiargs, "");                         //line:netp:parseuri:endextract
	strcpy(filename, ".");                           //line:netp:parseuri:beginconvert2
	strcat(filename, uri);                           //line:netp:parseuri:endconvert2
	return 0;
    }
}
void get_filetype(char *filename,char *filetype){
	if(strstr(filename,".html")){ strcpy(filetype,"text/html");}
	else if(strstr(filename,".gif")){ strcpy(filetype,"image/gif"); }
	else if(strstr(filename,".png")){ strcpy(filetype,"image/png"); }
	else if(strstr(filename,".jpg")){ strcpy(filetype,"image/jpeg"); }
	else{ strcpy(filetype,"text/plain"); }
	return;
}
void server_dynamic(int connfd,char *filename,char *cgiargs){
	char buf[MAXLINE],*emptylist[]={ NULL };
	sprintf(buf,"HTTP/1.0 200 OK\r\n");
	Rio_writen(connfd,buf,strlen(buf));
	sprintf(buf,"%sServer: Tiny Web Server\r\n",buf);
	Rio_writen(connfd,buf,strlen(buf));
	if(Fork()==0){
		setenv("QUERY_STRING",cgiargs,1);
		Dup2(connfd,STDOUT_FILENO);
		Execve(filename,emptylist,environ);
	}
	Wait(NULL);
	return;
}
void server_static(int connfd,char *filename,int filesize){
	int srcfd;
	char *srcp,filetype[MAXLINE],buf[MAXBUF];
	//先是相应报头
	get_filetype(filename,filetype);
	sprintf(buf,"HTTP/1.0 200 OK\r\n");
	sprintf(buf,"%sServer: Tiny Web Server\r\n",buf);
	sprintf(buf,"%sContent-length: %d\r\n",buf,filesize);
	sprintf(buf,"%sContent-type: %s\r\n\r\n",buf,filetype);
	/*两个换行以结束报头！！！！！！！！！！！！！！！！！！！！！！！！ */
	Rio_writen(connfd,buf,strlen(buf));
	//后是内容
	srcfd = Open(filename, O_RDONLY,0);
	srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd,0);
	Close(srcfd);
	Rio_writen(connfd, srcp, filesize);
	Munmap(srcp, filesize);
	return;
}
void doit(int connfd){
	struct stat sbuf; int is_static;
	char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE];
	char filename[MAXLINE],cgiargs[MAXLINE];
	rio_t rio;
	Rio_readinitb(&rio,connfd);
	Rio_readlineb(&rio,buf,MAXLINE);
	//printf("Request headers:\n");
	//printf("%s\n",buf);
	sscanf(buf,"%s %s %s",method,uri,version);
	if( strcasecmp(method,"GET") ){
		clienterror(connfd,method,"501","Not implemented","Tiny does not implement this method");
		return;
	}
	read_requesthdrs(&rio);
	is_static = parse_uri(uri,filename,cgiargs);
	if(stat(filename,&sbuf)<0){	
		clienterror(connfd,filename,"404","Not Found","Tiny does not implement this file");
		return;
	}
	if(is_static){
		if(!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode) ){
			clienterror(connfd,filename,"403","Forbidden","Tiny couldn't read the file");
			return;
		}
		server_static(connfd,filename,sbuf.st_size);
	}else{
		if(!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode) ){
			clienterror(connfd,filename,"403","Forbidden","Tiny couldn't run the file");
			return;
		}
		server_dynamic(connfd,filename,cgiargs);
	}
	return;
}
int main(int argc,char **argv){
	int listenfd,connfd;
	socklen_t len;
	struct sockaddr_storage clientaddr;
	char name[MAXLINE],port[MAXLINE];
	if(argc!=2){
		fprintf(stderr,"usage:%s <port>\n",argv[0]);
		exit(1);
	}
	listenfd=Open_listenfd( argv[1] );	
	while(1){
		len = sizeof( struct sockaddr_storage );
		connfd = Accept(listenfd,(SA *)&clientaddr,&len);
		getnameinfo((SA *)&clientaddr,len,name,MAXLINE,port,MAXLINE,0);
		printf("Accept connection from (%s,%s)\n",name,port);
		doit(connfd);//Do the follwing operation
		Close(connfd);
	}
	exit(0);
}
